package com.jingdianjichi.practice.server.service.impl;

import com.alibaba.fastjson.JSON;
import com.jingdianjichi.practice.api.common.PageInfo;
import com.jingdianjichi.practice.api.common.PageResult;
import com.jingdianjichi.practice.api.enums.IsDeletedFlagEnum;
import com.jingdianjichi.practice.api.req.GetPracticeSubjectsReq;
import com.jingdianjichi.practice.api.req.GetUnCompletePracticeReq;
import com.jingdianjichi.practice.api.resp.*;
import com.jingdianjichi.practice.server.entity.dto.CategoryDTO;
import com.jingdianjichi.practice.server.entity.dto.PracticeSetDTO;
import com.jingdianjichi.practice.server.entity.dto.PracticeSubjectDTO;
import com.jingdianjichi.practice.server.entity.po.*;
import com.jingdianjichi.practice.api.enums.CompleteStatusEnum;
import com.jingdianjichi.practice.api.enums.SubjectInfoTypeEnum;
import com.jingdianjichi.practice.server.mapper.*;
import com.jingdianjichi.practice.server.service.PracticeSetService;
import com.jingdianjichi.practice.server.util.DateUtils;
import com.jingdianjichi.practice.server.util.LoginUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;

@Service
@Slf4j
public class PracticeSetServiceImpl implements PracticeSetService {

    //分类
    @Resource
    private SubjectCategoryDao categoryDao;

    //题目映射表
    @Resource
    private SubjectMappingDao mappingDao;

    //标签
    @Resource
    private SubjectLabelDao labelDao;

    //题目表
    @Resource
    private PracticeSetDao practiceSetDao;

    //题目详情表
    @Resource
    private PracticeSetDetailDao practiceSetDetailDao;

    //题目表
    @Resource
    private SubjectDao subjectDao;

    //用户做大信息
    @Resource
    private PracticeDetailDao practiceDetailDao;

    @Resource
    private PracticeDao practiceDao;

    @Resource
    private SubjectRadioDao subjectRadioDao;

    @Resource
    private SubjectMultipleDao subjectMultipleDao;

    /**
     *查询大类在查小类,再查label,如果我们分类下没有提我们就不展示了亲,否则用户就乐了亲
     */
    @Override
    public List<SpecialPracticeVO> getSpecialPracticeContent() {
        List<SpecialPracticeVO> voList=new ArrayList<>();

        //做一波过滤,我们一会就去查这些题目亲
        List<Integer> subjectTypeList=new ArrayList<>();
        subjectTypeList.add(SubjectInfoTypeEnum.JUDGE.getCode());
        subjectTypeList.add(SubjectInfoTypeEnum.RADID.getCode());
        subjectTypeList.add(SubjectInfoTypeEnum.MULTIPLE.getCode());
        //subjectTypeList.add(SubjectInfoTypeEnum.BRIEF.getCode());

        //查询
        CategoryDTO categoryDTO = new CategoryDTO();
        categoryDTO.setSubjectTypeList(subjectTypeList);

        //查询所有的大类
        List<PrimaryCategoryPO> poList=categoryDao.getPrimaryCategory(categoryDTO);

        //如果没有这个集合的话就返回亲
        if(CollectionUtils.isEmpty(poList)){
            return voList;
        }

        //封装数据
        poList.forEach(primaryCategoryPO ->{
            SpecialPracticeVO specialPracticeVO = new SpecialPracticeVO();
            specialPracticeVO.setPrimaryCategoryId(primaryCategoryPO.getParentId());

            //查大类的岗位的名字
            CategoryPO categoryPO=categoryDao.selectById(primaryCategoryPO.getParentId());

            specialPracticeVO.setPrimaryCategoryName(categoryPO.getCategoryName());

            //只查大类下的小分类
            CategoryDTO categoryType = new CategoryDTO();
            categoryType.setCategoryType(2);
            categoryType.setParentId(primaryCategoryPO.getParentId());

            //查询小类
            List<CategoryPO> smallPoList=categoryDao.selectList(categoryType);

            //如果是空的直接下一个循环,去遍历下一个大类
            if (CollectionUtils.isEmpty(smallPoList)){
                return;
            }

            //查标签
            List<SpecialPracticeCategoryVO> categoryList=new ArrayList<>();

            smallPoList.forEach(smallPo->{
                //查标签
                List<SpecialPracticeLabelVO> labelVoList=getLabelVOList(smallPo.getId(),subjectTypeList);

                if (CollectionUtils.isEmpty(labelVoList)){
                    return;
                }

                //封装返回的数据
                SpecialPracticeCategoryVO specialPracticeCategoryVO = new SpecialPracticeCategoryVO();
                specialPracticeCategoryVO.setCategoryId(smallPo.getId());
                specialPracticeCategoryVO.setCategoryName(smallPo.getCategoryName());

                //封装标签信息
                List<SpecialPracticeLabelVO> labelList=new ArrayList<>();
                labelVoList.forEach(labelVO->{
                    SpecialPracticeLabelVO specialPracticeLabelVO = new SpecialPracticeLabelVO();
                    specialPracticeLabelVO.setId(labelVO.getId());
                    specialPracticeLabelVO.setAssembleId(labelVO.getAssembleId());
                    specialPracticeLabelVO.setLabelName(labelVO.getLabelName());

                    labelList.add(specialPracticeLabelVO);
                });

                specialPracticeCategoryVO.setLabelList(labelList);
                categoryList.add(specialPracticeCategoryVO);
            });

            specialPracticeVO.setCategoryList(categoryList);
            voList.add(specialPracticeVO);
        });

        return voList;
    }

    //获得所有的标签
    private List<SpecialPracticeLabelVO> getLabelVOList(Long categoryId, List<Integer> subjectTypeList) {
        List<LabelCountPO> countPOList =mappingDao.getLabelSubjectCount(categoryId,subjectTypeList);

        if (CollectionUtils.isEmpty(countPOList)){
            return Collections.emptyList();
        }

        //返回数据
        List<SpecialPracticeLabelVO> voList=new ArrayList<>();
        countPOList.forEach(po->{
            SpecialPracticeLabelVO specialPracticeLabelVO = new SpecialPracticeLabelVO();
            specialPracticeLabelVO.setId(po.getLabelId());
            specialPracticeLabelVO.setAssembleId(categoryId+"-"+ po.getLabelId());

            //查询分类的名称
            SubjectLabelPO subjectLabelPO = labelDao.queryById(po.getLabelId());
            specialPracticeLabelVO.setLabelName(subjectLabelPO.getLabelName());

            voList.add(specialPracticeLabelVO);
        });

        return voList;
    }


    /**
     * 开始练习
     */
    @Override
    @Transactional
    public PracticeSetVO addPractice(PracticeSubjectDTO dto) {
        //创建返回的对象
        PracticeSetVO setVO = new PracticeSetVO();

        //获得套卷的详细信息,一套题我们要知道所有的题目信息,返回的是题目的id和题目的类型
        List<PracticeSubjectDetailVO> practiceList=getPracticeList(dto);

        //判空
        if (CollectionUtils.isEmpty(practiceList)){
            return setVO;
        }

        //套卷信息哥数据库交互
        PracticeSetPO practiceSetPO = new PracticeSetPO();

        //套卷类行,我是实时生成
        practiceSetPO.setSetType(1);

        //获得前端的标签列表,我们默认把勾选的分类作为题目的名称亲
        List<String> assembleIds = dto.getAssembleIds();

        Set<Long> categoryIdSet=new HashSet<>();

        //获得分类id,我们把分类名字作为套卷的名称
        assembleIds.forEach(assembleId->{
            //分离id,只有小类的
            Long categoryId = Long.valueOf(assembleId.split("-")[0]);

            //添加分类id
            categoryIdSet.add(categoryId);
        });

        //拼接试卷的题目
        StringBuffer setName = new StringBuffer();

        int i=1;
        for (Long categoryId : categoryIdSet) {
            if (i > 2){
                //我们的题目最多两个分类亲
                break;
            }

            //查询分类信息
            CategoryPO categoryPO=categoryDao.selectById(categoryId);

            //添加题目名字
            setName.append(categoryPO.getCategoryName());
            setName.append("#");

            i++;
        }

        //把最后一次循环的#删除掉
        setName.deleteCharAt(setName.length()-1);

        //拼接题目
        if (i==2){
            setName.append("专项练习");
        }else {
            setName.append("等专项练习");
        }

        //封装名字
        practiceSetPO.setSetName(setName.toString());

        //获得标签id
        String labelId = assembleIds.get(0).split("-")[1];

        //标签信息
        SubjectLabelPO labelPO = labelDao.queryById(Long.valueOf(labelId));

        //封装岗位信息
        practiceSetPO.setPrimaryCategoryId(labelPO.getCategoryId());
        practiceSetPO.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());

        practiceSetPO.setCreatedBy(LoginUtil.getLoginId());     //封装用户信息
        practiceSetPO.setCreatedTime(new Date());

        //添加信息
        practiceSetDao.add(practiceSetPO);

        //套卷的id回显
        Long practiceSetId = practiceSetPO.getId();


        //添加套卷详情表,可以批量插入的亲
        practiceList.forEach(e->{
            //详细信息表
            PracticeSetDetailPO detailPO = new PracticeSetDetailPO();
            detailPO.setSetId(practiceSetId);
            detailPO.setSubjectId(e.getSubjectId());
            detailPO.setSubjectType(e.getSubjectType());
            detailPO.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
            detailPO.setCreatedBy(LoginUtil.getLoginId());
            detailPO.setCreatedTime(new Date());

            //添加题目详情
            practiceSetDetailDao.add(detailPO);
        });

        //套卷id
        setVO.setSetId(practiceSetId);

        return setVO;
    }



    //获得套卷的题目信息,题目的id和题目的类型
    private List<PracticeSubjectDetailVO> getPracticeList(PracticeSubjectDTO dto) {
        //构建返回参数
        List<PracticeSubjectDetailVO> practiceSubjectDetailVOList=new ArrayList<>();

        //避免重复,因为我们在补充题目的时候,我们是有20个题的话,但是我们凑不出来20个题的话,我们就可以补足去
        //这个就是先把查出来的放进来,防止补足的时候补充重复的题目亲
        List<Long> excludeSubjectIds=new ArrayList<>();

        //设置套卷的数量我们后面改为nacos的动态配置
        Integer radioSubjectCount = 10;
        Integer multipleSubjectCount = 6;
        Integer judgeSubjectCount = 4;
        Integer totalSubjectCount = 20;         //总题目亲

        //现在我们的dto中只有assembleIds这个腻

        //查询单选
        dto.setSubjectCount(radioSubjectCount);                         //查询的数量
        dto.setSubjectType(SubjectInfoTypeEnum.RADID.getCode());        //题目类型
        assembleList(dto,practiceSubjectDetailVOList,excludeSubjectIds);

        //查询多选
        dto.setSubjectCount(multipleSubjectCount);
        dto.setSubjectType(SubjectInfoTypeEnum.MULTIPLE.getCode());
        assembleList(dto, practiceSubjectDetailVOList, excludeSubjectIds);

        //查询判断
        dto.setSubjectCount(judgeSubjectCount);
        dto.setSubjectType(SubjectInfoTypeEnum.JUDGE.getCode());
        assembleList(dto, practiceSubjectDetailVOList, excludeSubjectIds);

        //补充题目
        if(practiceSubjectDetailVOList.size() == totalSubjectCount){    //如果够20个就表示,不用补充题目了
            return practiceSubjectDetailVOList;
        }

        //这个就是补充的题目
        int remainCount = totalSubjectCount - practiceSubjectDetailVOList.size();       //这个即使差的数量
        dto.setSubjectCount(remainCount);                                               //设置查询的数量
        dto.setSubjectType(1);                                                          //我们设置的补充的题目是单选类型的这个后面也是可以动态配置的
        assembleList(dto,practiceSubjectDetailVOList,excludeSubjectIds);

        //返回题目信息数据亲
        return practiceSubjectDetailVOList;
    }


    //查询规定的题目,并放入到list中
    private List<PracticeSubjectDetailVO> assembleList(PracticeSubjectDTO dto, List<PracticeSubjectDetailVO> list, List<Long> excludeSubjectIds) {
        //我们查的时候我们一定要把查询的已经去给排除掉的
        dto.setExcludeSubjectIds(excludeSubjectIds);

        //获得题目信息
        List<SubjectPO> subjectPOList=subjectDao.getPracticeSubject(dto);

        if (CollectionUtils.isEmpty(subjectPOList)){
            return list;
        }

        //添加已经查询的题目避免重复查询,并且添加到现有的题目列表中
        subjectPOList.forEach(e->{
            PracticeSubjectDetailVO vo = new PracticeSubjectDetailVO();

            //存入题目的id和题目的类型
            vo.setSubjectId(e.getId());
            vo.setSubjectType(e.getSubjectType());

            //添加排查的id
            excludeSubjectIds.add(e.getId());

            //添加题目
            list.add(vo);
        });

        return list;
    }


    //获得练习题
    @Override
    public PracticeSubjectListVO getSubjects(GetPracticeSubjectsReq req) {
        //套卷id
        Long setId = req.getSetId();

        //返回套卷的名称和题目,题目包括题目的id和题目的类型
        PracticeSubjectListVO vo = new PracticeSubjectListVO();

        List<PracticeSubjectDetailVO> practiceSubjectListVOS = new LinkedList<>();

        //查询题目的类型
        List<PracticeSetDetailPO> practiceSetDetailPOS = practiceSetDetailDao.selectBySetId(setId);

        if (CollectionUtils.isEmpty(practiceSetDetailPOS)) {
            return vo;
        }

        //用户id
        String loginId = LoginUtil.getLoginId();

        //套卷id
        Long practiceId = req.getPracticeId();

        //变量题目
        practiceSetDetailPOS.forEach(e -> {
            //封装题目的信息
            PracticeSubjectDetailVO practiceSubjectListVO = new PracticeSubjectDetailVO();

            //题目id和题目类型
            practiceSubjectListVO.setSubjectId(e.getSubjectId());
            practiceSubjectListVO.setSubjectType(e.getSubjectType());

            /**
             * 这个是每个题目的做大状态
             */
            //判断用户练习的状态
            if (Objects.nonNull(practiceId)) {
                //查询用户对应套卷的对应题目的信息
                PracticeDetailPO practiceDetailPO = practiceDetailDao.selectDetail(practiceId, e.getSubjectId(), loginId);

                //获得用户的做大内容
                if (Objects.nonNull(practiceDetailPO) && StringUtils.isNotBlank(practiceDetailPO.getAnswerContent())) {
                    //这里就是答题了
                    practiceSubjectListVO.setIsAnswer(1);
                    //把用户的答案返回去
                    practiceSubjectListVO.setAnswerContent(practiceDetailPO.getAnswerContent());
                } else {
                    //这里就是没答题
                    practiceSubjectListVO.setIsAnswer(0);

                }
            }

            //添加,附加用户作答状态
            practiceSubjectListVOS.add(practiceSubjectListVO);
        });

        //设置题目
        vo.setSubjectList(practiceSubjectListVOS);

        //反查一下
        PracticeSetPO practiceSetPO = practiceSetDao.selectById(setId);

        //设置题目
        vo.setTitle(practiceSetPO.getSetName());

        /**
         * 下面是套卷的整体做大状态
         */
        //如果前端传递了practiceId,表示用户是第一次做大的
        if (Objects.isNull(practiceId))
        {
            //添加用户作答信息到practice_info表,传递套卷的id
            Long newPracticeId = insertUnCompletePractice(setId);

            //因为是第一次做,我们返回用户的做题情况也就四practice_info表的主键
            vo.setPracticeId(newPracticeId);
        }
        else
        {
            //中途返回
            //如果不是空的就是表示用户是中图返回来做大的
            updateUnCompletePractice(practiceId);       //这个就是用户的套卷作答状态的id

            PracticePO practicePO = practiceDao.selectById(practiceId);

            //设置用户目前的用时时间
            vo.setTimeUse(practicePO.getTimeUse());     //就是从刚才插入的数据中拿回来的
            vo.setPracticeId(practiceId);
        }


        //返回数据
        return vo;
    }

    //插入用户的套卷信息到practice_info表
    private Long insertUnCompletePractice(Long practiceSetId) {
        PracticePO practicePO = new PracticePO();

        practicePO.setSetId(practiceSetId);                                                     //套卷id
        practicePO.setCompleteStatus(CompleteStatusEnum.NO_COMPLETE.getCode());                 //完成状态未完成,第一次做大
        practicePO.setTimeUse("00:00:00");                                                      //下面都是初始化数据
        practicePO.setSubmitTime(new Date());
        practicePO.setCorrectRate(new BigDecimal("0.00"));                                  //正确率
        practicePO.setIsDeleted(com.jingdianjichi.practice.api.enums.IsDeletedFlagEnum.UN_DELETED.getCode());
        practicePO.setCreatedBy(LoginUtil.getLoginId());
        practicePO.setCreatedTime(new Date());

        //插入信息
        practiceDao.insert(practicePO);

        //返回主键
        return practicePO.getId();
    }

    //更新用户套卷的做大状态
    private void updateUnCompletePractice(Long practiceId) {
        PracticePO practicePO = new PracticePO();
        practicePO.setId(practiceId);
        practicePO.setSubmitTime(new Date());           //时间更为当前时间

        practiceDao.update(practicePO);
    }

    //获得题目的详细信息
    @Override
    public PracticeSubjectVO getPracticeSubject(PracticeSubjectDTO dto) {
        PracticeSubjectVO practiceSubjectVO = new PracticeSubjectVO();

        //根据题目id查询题目的基本信息
        SubjectPO subjectPO = subjectDao.selectById(dto.getSubjectId());

        //设置题目的名称和类型
        practiceSubjectVO.setSubjectName(subjectPO.getSubjectName());
        practiceSubjectVO.setSubjectType(subjectPO.getSubjectType());

        //填充单选和多选的答案,我们可以做出策略+工程的亲
        //这里是单选
        if (dto.getSubjectType() == SubjectInfoTypeEnum.RADID.getCode()) {
            //返回值来的
            List<PracticeSubjectOptionVO> optionList = new LinkedList<>();

            //查询单选的选项
            List<SubjectRadioPO> radioSubjectPOS = subjectRadioDao.selectBySubjectId(subjectPO.getId());

            //封装信息返回
            radioSubjectPOS.forEach(e -> {
                PracticeSubjectOptionVO practiceSubjectOptionVO = new PracticeSubjectOptionVO();
                practiceSubjectOptionVO.setOptionContent(e.getOptionContent());     //选项内容
                practiceSubjectOptionVO.setOptionType(e.getOptionType());           //选项abcd

                optionList.add(practiceSubjectOptionVO);
            });

            //设置答案列表
            practiceSubjectVO.setOptionList(optionList);
        }

        //这里是多选来的
        if (dto.getSubjectType() == SubjectInfoTypeEnum.MULTIPLE.getCode()) {
            List<PracticeSubjectOptionVO> optionList = new LinkedList<>();

            //查询多选逻辑和上面一样的
            List<SubjectMultiplePO> multipleSubjectPOS = subjectMultipleDao.selectBySubjectId(subjectPO.getId());
            multipleSubjectPOS.forEach(e -> {
                //封装数据
                PracticeSubjectOptionVO practiceSubjectOptionVO = new PracticeSubjectOptionVO();
                practiceSubjectOptionVO.setOptionContent(e.getOptionContent());
                practiceSubjectOptionVO.setOptionType(e.getOptionType());

                optionList.add(practiceSubjectOptionVO);
            });

            practiceSubjectVO.setOptionList(optionList);
        }

        //返回数据
        return practiceSubjectVO;
    }

    //模拟套题列表
    @Override
    public PageResult<PracticeSetVO> getPreSetContent(PracticeSetDTO dto) {
        //构造返回值
        PageResult<PracticeSetVO> pageResult = new PageResult<>();
        PageInfo pageInfo = dto.getPageInfo();

        //设置分页参数
        pageResult.setPageNo(pageInfo.getPageNo());
        pageResult.setPageSize(pageInfo.getPageSize());
        int start = (pageInfo.getPageNo() - 1) * pageInfo.getPageSize();

        //计算总数
        Integer count = practiceSetDao.getListCount(dto);

        if (count == 0) {
            return pageResult;
        }

        //分页查询
        List<PracticeSetPO> setPOList = practiceSetDao.getSetList(dto, start, dto.getPageInfo().getPageSize());
        if (log.isInfoEnabled()) {
            log.info("获取的模拟考卷列表{}", JSON.toJSONString(setPOList));
        }

        //封装实体返回
        List<PracticeSetVO> list = new LinkedList<>();
        setPOList.forEach(e -> {
            PracticeSetVO vo = new PracticeSetVO();
            vo.setSetId(e.getId());
            vo.setSetName(e.getSetName());
            vo.setSetHeat(e.getSetHeat());
            vo.setSetDesc(e.getSetDesc());

            //添加套题id
            list.add(vo);
        });

        //封装分页数据
        pageResult.setRecords(list);
        pageResult.setTotal(count);

        //返回
        return pageResult;
    }

    //获得用户未完成的套题
    @Override
    public PageResult<UnCompletePracticeSetVO> getUnCompletePractice(GetUnCompletePracticeReq req) {
        PageResult<UnCompletePracticeSetVO> pageResult = new PageResult<>();

        //封装分页信息
        PageInfo pageInfo = req.getPageInfo();
        pageResult.setPageNo(pageInfo.getPageNo());
        pageResult.setPageSize(pageInfo.getPageSize());
        int start = (pageInfo.getPageNo() - 1) * pageInfo.getPageSize();

        //用户id
        String loginId = LoginUtil.getLoginId();
        //为完成总数
        Integer count = practiceDao.getUnCompleteCount(loginId);
        if (count == 0) {
            return pageResult;
        }

        //用户没有完成的分页信息
        List<PracticePO> poList = practiceDao.getUnCompleteList(loginId, start, req.getPageInfo().getPageSize());
        if (log.isInfoEnabled()) {
            log.info("获取未完成的考卷列表{}", JSON.toJSONString(poList));
        }

        //添加反悔哦信息
        List<UnCompletePracticeSetVO> list = new LinkedList<>();
        poList.forEach(e -> {
            UnCompletePracticeSetVO vo = new UnCompletePracticeSetVO();
            vo.setSetId(e.getSetId());          //套题id
            vo.setPracticeId(e.getId());
            vo.setPracticeTime(DateUtils.format(e.getSubmitTime(), "yyyy-MM-dd"));

            //获得套题名称
            PracticeSetPO practiceSetPO = practiceSetDao.selectById(e.getSetId());
            vo.setTitle(practiceSetPO.getSetName());


            list.add(vo);
        });

        //分页参数
        pageResult.setRecords(list);
        pageResult.setTotal(count);

        //返回
        return pageResult;
    }


}
